{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "pytorch_quick_start.ipynb",
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9XHd5ExbUIUg",
        "colab_type": "text"
      },
      "source": [
        "# PyTorch 1.2 Quickstart with Google Colab\n",
        "\n",
        "> \"Learn about PyTorch's basic building blocks to build and train a CNN model for image classification.\"\n",
        "\n",
        "- toc: false\n",
        "- branch: master\n",
        "- author: Elvis Saravia\n",
        "- badges: true\n",
        "- comments: true\n",
        "- categories: [deep learning, cnn, neural network, image classification, intermediate, pytorch]\n",
        "- image: images/pytorch-quick.png\n",
        "- hide: false"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7TVr2t_ntPPD",
        "colab_type": "text"
      },
      "source": [
        "## About\n",
        "\n",
        "In this code tutorial we will learn how to quickly train a model to understand some of PyTorch's basic building blocks to train a deep learning model. This notebook is inspired by the [\"Tensorflow 2.0 Quickstart for experts\"](https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/quickstart/advanced.ipynb#scrollTo=DUNzJc4jTj6G) notebook. \n",
        "\n",
        "After completion of this tutorial, you should be able to import data, transform it, and efficiently feed the data in batches to a convolution neural network (CNN) model for image classification.\n",
        "\n",
        "**Author:** [Elvis Saravia](https://twitter.com/omarsar0)"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Su0COdCqT2Wk",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "## import libraries\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "import torchvision\n",
        "import torchvision.transforms as transforms"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "rXCYmmjyVRq5",
        "colab_type": "code",
        "outputId": "8c90ba8a-692c-45b4-f89b-000735bccf1e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "print(torch.__version__)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "1.4.0\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hhuQyU7AYE6K",
        "colab_type": "text"
      },
      "source": [
        "## Import The Data\n",
        "The first step before training the model is to import the data. We will use the [MNIST dataset](http://yann.lecun.com/exdb/mnist/) which is like the Hello World dataset of machine learning. \n",
        "\n",
        "Besides importing the data, we will also do a few more things:\n",
        "- We will tranform the data into tensors using the `transforms` module\n",
        "- We will use `DataLoader` to build convenient data loaders or what are referred to as iterators, which makes it easy to efficiently feed data in batches to deep learning models. \n",
        "- As hinted above, we will also create batches of the data by setting the `batch` parameter inside the data loader. Notice we use batches of `32` in this tutorial but you can change it to `64` if you like. I encourage you to experiment with different batches."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "tSjjLXrOVWBy",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "%%capture\n",
        "BATCH_SIZE = 32\n",
        "\n",
        "## transformations\n",
        "transform = transforms.Compose(\n",
        "    [transforms.ToTensor()])\n",
        "\n",
        "## download and load training dataset\n",
        "trainset = torchvision.datasets.MNIST(root='./data', train=True,\n",
        "                                        download=True, transform=transform)\n",
        "trainloader = torch.utils.data.DataLoader(trainset, batch_size=BATCH_SIZE,\n",
        "                                          shuffle=True, num_workers=2)\n",
        "\n",
        "## download and load testing dataset\n",
        "testset = torchvision.datasets.MNIST(root='./data', train=False,\n",
        "                                       download=True, transform=transform)\n",
        "testloader = torch.utils.data.DataLoader(testset, batch_size=BATCH_SIZE,\n",
        "                                         shuffle=False, num_workers=2)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0nZwZukWXUDn",
        "colab_type": "text"
      },
      "source": [
        "## Exploring the Data\n",
        "As a practioner and researcher, I am always spending a bit of time and effort exploring and understanding the dataset. It's fun and this is a good practise to ensure that everything is in order. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NW_loWKga7CH",
        "colab_type": "text"
      },
      "source": [
        "Let's check what the train and test dataset contains. I will use `matplotlib` to print out some of the images from our dataset. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "zWd9Pt1Ca6K9",
        "colab_type": "code",
        "outputId": "ea0d0d32-7404-4fd0-8af7-fb5dab06477b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 220
        }
      },
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "## functions to show an image\n",
        "def imshow(img):\n",
        "    #img = img / 2 + 0.5     # unnormalize\n",
        "    npimg = img.numpy()\n",
        "    plt.imshow(np.transpose(npimg, (1, 2, 0)))\n",
        "\n",
        "## get some random training images\n",
        "dataiter = iter(trainloader)\n",
        "images, labels = dataiter.next()\n",
        "\n",
        "## show images\n",
        "imshow(torchvision.utils.make_grid(images))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\nx9TW1rq9H+21olQq8fHx4ZZbbpG1yW+0o9mNIkkSKpWKuLg4vL298fHxYd68eaSkpKBWqxkcHKSw\nsJC+vj6USiWZmZmMjIzQ2NhIdXU1PT09brXfWdxUzl2r1ZKYmMjixYu5/fbbCQoKQqFQYLFYKCkp\nob6+fs5+kSaTCT8/P8LCwlizZg133XWX3ArNarVis9kICAhg1apV+Pj4YLPZ6OnpcXmYSZIkgoOD\nMRgM6PV6fH19gelGHt3d3W7r6GMwGPD19SUqKoqlS5eyYsUKFixYgBAClUqFQqFAkiT8/f3p7e1F\np9OhULi+nkCSJBYtWsSSJUuIiopieHiYsbExl7bZ+6ZoNBq8vLwICwvDz8+P4OBgUlJSOHToEA0N\nDXR1deHn5yd3QnLVrFKj0aBWq9Hr9ZhMJlasWIG/vz/+/v4sXLiQ+fPno9FoZKfe3d2NSqUiLy+P\n4eFhKioqMBqNFBUV3ZwzYUe37qttwOtAF3D+kn0BQCFQe/Gn/8X9EvA8UAecA3K+7v0vvk7c6KZQ\nKERsbKx48cUXRUNDg7DZbMJqtYrBwUFx/vx5UVBQIHx9fW/4c5yxKRQKce+994qXX35ZlJSUiMHB\nQTE5OSmsVquYnJwUra2tora2VjQ2Ngqr1Sreeecd8eijj4qQkBCX2ilJkvDy8hKbN28WL774oti7\nd6+w2+3CbreLDz/8UNx///3ionyzy7fc3Fzx9NNPi5MnTwqLxSKsVquwWq1iampK1NTUiBMnToh9\n+/aJf/mXfxGrV68WYWFhbrFTrVaLnTt3ioGBAWGz2URra6v4u7/7O2Eymdx+Hl5tCw8PF2vXrhUf\nfvihMJvNYnx8XFitVvHWW2+J9evXC4PBIO655x6RmJjosmtMoVCIiIgIkZOTI+677z7xwgsviOHh\nYfl8dGw2m+2yvx1bX1+fOHbsmPD29hYKhcLtx/k6t1NX86vXMnLfCrwIvHXJvp8Be4QQv5Ek6WcX\n//5n4HYg8eK2EHj54k+nEh4eTk5ODk8//TRpaWly+7X+/n527drFe++9R0lJidunj1/GaDQSGRnJ\nokWLePjhh0lKSiIkJARJkmhqauLMmTMUFRVx4cIFfHx8yMjI4Kc//SnLli2jsbGRQ4cOucROhUJB\nZGQkubm53HbbbWzcuFHuPu8IacybNw+FQsHQ0BCnTp2ir6/PJbapVCoWL17M97//fZYsWUJoaCjj\n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\nVk6cOEFxcTEnTpxgcHDQGR/7jXGMhI1GI8nJyWRmZpKWlobVamV4eJimpiYOHz5MRUUF/f39V30f\npVKJl5cXQUFBSJIkxw9n087IyEiys7NZt24deXl5qNVqLBYL1dXVFBUVceDAAQ4fPgxM1+NrtVqS\nkpJQqaZPI6vVSn19PcPDw04PyURGRpKQkEBCQgKSJNHQ0MCJEyc4cODAnMyvdHZ2UlVVxfDw8GWx\n4LGxMUZGRtxo3Uz8/f2JiYkhMTGRiooKfHx8iI+PZ+3atSQkJODr6yuPRMfGxuSwiMVimXVblEol\ncXFxZGZmYjAYKCkp4dChQxQVFXHq1CkAAgMDyc3NZdWqVdxyyy1y+XBDQwPd3d20tbWxf/9+zGbz\ndc/kHfklRz7pUuduMBjw8fGRrwMHDod/6aheoVCgVCrR6XSo1Wp8fHxISkpCo9Fcl11fxdc6d0mS\ntjGdPA2UJKkF+A+mnfr7kiQ9CZiBBy8+fRewgelSSAvwxKxbPG0TRqOR2267TT7RxsfHefHFFzl9\n+jQdHR1f+VoHrli8olKp8PX1JTU1lXXr1sk3JEed7f79+3nppZfo6en5Snu8vLzw8/PDZDJht9vp\n7++nra1t1v4HtVrN6tWrWbduHQ888AAwPRLv7Ozk9ddf5/33359Rwx4eHk5cXBwmkwmlUinLO5SX\nl7skmbpq1Spyc3PlComioiI++eQTzp4969TPvV4GBwdpamqip6dHDiM56Orqorm5+Ste7VoCAgII\nCwtDp9ORnJxMQkIC9913n5xzsdls8nVktVoZHx932s3Jy8sLk8kkz863bt3KwYMHOX/+PAAxMTEs\nW7aMf/u3fyMuLg6lUondbmd4eJh3332Xo0ePyvmXG7lWbDYbw8PD7N27F6vVOmNmmpiYSEZGBt7e\n3jNe4wjZXIpGo8FgMBARESEP/AIDA2fIjMwW11It892rPLTmCs8VwJYbNerrSE1NZdmyZaSkpKDR\naGhoaGD//v2cPHnyilMbSZLQ6/XExMTIJWiTk5O0trbS3d3tVFuDg4NZunQpL730ElqtFpjO2h85\ncoRf//rXnDlzhrGxsa898YKCgggPD0ev19PS0sLx48f54osvbtg+SZLw9fXlySefZNOmTSQkJADT\njv2zzz7js88+4+2335ZDXJIkER0dzT333MPq1avx9vZGkiSam5spKSnhxRdfdOqsyZF32LhxI/n5\n+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\nQmCC\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XFWll5Lseiht",
        "colab_type": "text"
      },
      "source": [
        "**EXERCISE:** Try to understand what the code above is doing. This will help you to better understand your dataset before moving forward. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "d9mXAVmRvhrq",
        "colab_type": "text"
      },
      "source": [
        "Let's check the dimensions of a batch."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cNFKWz1GZ4R5",
        "colab_type": "code",
        "outputId": "8c6c2761-3bd3-45cb-e07f-e3fe2c3f3de7",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "for images, labels in trainloader:\n",
        "    print(\"Image batch dimensions:\", images.shape)\n",
        "    print(\"Image label dimensions:\", labels.shape)\n",
        "    break"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Image batch dimensions: torch.Size([32, 1, 28, 28])\n",
            "Image label dimensions: torch.Size([32])\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tmaCTw5tXowR",
        "colab_type": "text"
      },
      "source": [
        "## The Model\n",
        "Now using the classical deep learning framework pipeline, let's build the 1 convolutional layer model. \n",
        "\n",
        "Here are a few notes for those who are beginning with PyTorch:\n",
        "- The model below consists of an `__init__()` portion which is where you include the layers and components of the neural network. In our model, we have a convolutional layer denoted by `nn.Conv2d(...)`. We are dealing with an image dataset that is in a grayscale so we only need one channel going in, hence `in_channels=1`. We hope to get a nice representation of this layer, so we use `out_channels=32`. Kernel size is 3, and for the rest of parameters we use the default values which you can find [here](https://pytorch.org/docs/stable/nn.html?highlight=conv2d#conv2d). \n",
        "- We use 2 back to back dense layers or what we refer to as linear transformations to the incoming data. Notice for `d1` I have a dimension which looks like it came out of nowhere. 128 represents the size we want as output and the (`26*26*32`) represents the dimension of the incoming data. If you would like to find out how to calculate those numbers refer to the [PyTorch documentation](https://pytorch.org/docs/stable/nn.html?highlight=linear#conv2d). In short, the convolutional layer transforms the input data into a specific dimension that has to be considered in the linear layer. The same applies for the second linear transformation (`d2`) where the dimension of the output of the previous linear layer was added as `in_features=128`, and `10` is just the size of the output which also corresponds to the number of classes.\n",
        "- After each one of those layers, we also apply an activation function such as `ReLU`. For prediction purposes, we then apply a `softmax` layer to the last transformation and return the output of that.  "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_IYnV4ZBa3cJ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "class MyModel(nn.Module):\n",
        "    def __init__(self):\n",
        "        super(MyModel, self).__init__()\n",
        "\n",
        "        # 28x28x1 => 26x26x32\n",
        "        self.conv1 = nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3)\n",
        "        self.d1 = nn.Linear(26 * 26 * 32, 128)\n",
        "        self.d2 = nn.Linear(128, 10)\n",
        "\n",
        "    def forward(self, x):\n",
        "        # 32x1x28x28 => 32x32x26x26\n",
        "        x = self.conv1(x)\n",
        "        x = F.relu(x)\n",
        "\n",
        "        # flatten => 32 x (32*26*26)\n",
        "        x = x.flatten(start_dim = 1)\n",
        "\n",
        "        # 32 x (32*26*26) => 32x128\n",
        "        x = self.d1(x)\n",
        "        x = F.relu(x)\n",
        "\n",
        "        # logits => 32x10\n",
        "        logits = self.d2(x)\n",
        "        out = F.softmax(logits, dim=1)\n",
        "        return out"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "evsFbkq_X6bc",
        "colab_type": "text"
      },
      "source": [
        "As I have done in my previous tutorials, I always encourage to test the model with 1 batch to ensure that the output dimensions are what we expect. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "1poxFYqftKov",
        "colab_type": "code",
        "outputId": "aac57b4b-10c1-40bb-fea7-e6ca12c74570",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 51
        }
      },
      "source": [
        "## test the model with 1 batch\n",
        "model = MyModel()\n",
        "for images, labels in trainloader:\n",
        "    print(\"batch size:\", images.shape)\n",
        "    out = model(images)\n",
        "    print(out.shape)\n",
        "    break"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "batch size: torch.Size([32, 1, 28, 28])\n",
            "torch.Size([32, 10])\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9h_3eZQRHV_P",
        "colab_type": "text"
      },
      "source": [
        "## Training the Model\n",
        "Now we are ready to train the model but before that we are going to setup a loss function, an optimizer and a function to compute accuracy of the model. "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "3_0Vjq2RHlph",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "learning_rate = 0.001\n",
        "num_epochs = 5\n",
        "\n",
        "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n",
        "model = MyModel()\n",
        "model = model.to(device)\n",
        "criterion = nn.CrossEntropyLoss()\n",
        "optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "44IdrNNeIi_I",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "## compute accuracy\n",
        "def get_accuracy(logit, target, batch_size):\n",
        "    ''' Obtain accuracy for training round '''\n",
        "    corrects = (torch.max(logit, 1)[1].view(target.size()).data == target.data).sum()\n",
        "    accuracy = 100.0 * corrects/batch_size\n",
        "    return accuracy.item()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "nK3EcuIOISSR",
        "colab_type": "text"
      },
      "source": [
        "Now it's time for training."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "E59hwZlAIVcL",
        "colab_type": "code",
        "outputId": "e720f2db-345f-4925-cdec-9541a4a3208b",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        }
      },
      "source": [
        "for epoch in range(num_epochs):\n",
        "    train_running_loss = 0.0\n",
        "    train_acc = 0.0\n",
        "\n",
        "    model = model.train()\n",
        "\n",
        "    ## training step\n",
        "    for i, (images, labels) in enumerate(trainloader):\n",
        "        \n",
        "        images = images.to(device)\n",
        "        labels = labels.to(device)\n",
        "\n",
        "        ## forward + backprop + loss\n",
        "        logits = model(images)\n",
        "        loss = criterion(logits, labels)\n",
        "        optimizer.zero_grad()\n",
        "        loss.backward()\n",
        "\n",
        "        ## update model params\n",
        "        optimizer.step()\n",
        "\n",
        "        train_running_loss += loss.detach().item()\n",
        "        train_acc += get_accuracy(logits, labels, BATCH_SIZE)\n",
        "    \n",
        "    model.eval()\n",
        "    print('Epoch: %d | Loss: %.4f | Train Accuracy: %.2f' \\\n",
        "          %(epoch, train_running_loss / i, train_acc/i))        "
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Epoch: 0 | Loss: 1.5831 | Train Accuracy: 88.24\n",
            "Epoch: 1 | Loss: 1.4956 | Train Accuracy: 96.91\n",
            "Epoch: 2 | Loss: 1.4834 | Train Accuracy: 98.03\n",
            "Epoch: 3 | Loss: 1.4784 | Train Accuracy: 98.52\n",
            "Epoch: 4 | Loss: 1.4751 | Train Accuracy: 98.81\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "QuZxfQc1UIU-",
        "colab_type": "text"
      },
      "source": [
        "We can also compute accuracy on the testing dataset to see how well the model performs on the image classificaiton task. As you can see below, our basic CNN model is performing very well on the MNIST classification task."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "YU5WR0BTUHv1",
        "colab_type": "code",
        "outputId": "86cc4813-f1e6-4952-e1d4-66349b755b2c",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "test_acc = 0.0\n",
        "for i, (images, labels) in enumerate(testloader, 0):\n",
        "    images = images.to(device)\n",
        "    labels = labels.to(device)\n",
        "    outputs = model(images)\n",
        "    test_acc += get_accuracy(outputs, labels, BATCH_SIZE)\n",
        "        \n",
        "print('Test Accuracy: %.2f'%( test_acc/i))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Test Accuracy: 98.36\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BZz7LAewgGAK",
        "colab_type": "text"
      },
      "source": [
        "**EXERCISE:** As a way to practise, try to include the testing part inside the code where I was outputing the training accuracy, so that you can also keep testing the model on the testing data as you proceed with the training steps. This is useful as sometimes you don't want to wait until your model has completed training to actually test the model with the testing data."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uLQlqGPsVjOB",
        "colab_type": "text"
      },
      "source": [
        "## Final Words\n",
        "That's it for this tutorial! Congratulations! You are now able to implement a basic CNN model in PyTorch for image classification. If you would like, you can further extend the CNN model by adding more convolution layers and max pooling, but as you saw, you don't really need it here as results look good. If you are interested in implementing a similar image classification model using RNNs see the references below. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ztAiTq9HcS_H",
        "colab_type": "text"
      },
      "source": [
        "## References\n",
        "- [Building RNNs is Fun with PyTorch and Google Colab](https://colab.research.google.com/drive/1NVuWLZ0cuXPAtwV4Fs2KZ2MNla0dBUas)\n",
        "- [CNN Basics with PyTorch by Sebastian Raschka](https://github.com/rasbt/deeplearning-models/blob/master/pytorch_ipynb/cnn/cnn-basic.ipynb)\n",
        "- [Tensorflow 2.0 Quickstart for experts](https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/tutorials/quickstart/advanced.ipynb#scrollTo=DUNzJc4jTj6G) "
      ]
    }
  ]
}